import { JsonRpcProvider, Wallet, parseEther, Contract, parseUnits } from "ethers"; // 导入 ethers

import fs from "fs";
import dotenv from "dotenv";
import { Worker, isMainThread, parentPort, workerData } from "worker_threads"; // 增加 workerData 用于传递线程编号
import { fileURLToPath } from 'url';  // 解析 ES 模块的文件路径
import { dirname } from 'path';       // 获取文件所在目录

// 加载 .env 文件中的环境变量
dotenv.config();

// 手动定义 __filename 和 __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const PRIVATE_KEY = process.env.MAIN_PRIVATE_KEY; // 从 .env 文件中获取主号私钥
const MAIN_ACCOUNT = process.env.MAIN_ACCOUNT; // 从 .env 文件中获取主号地址
const CONTRACT_ADDRESS = "0x2f16386bb37709016023232523ff6d9daf444be3"; //  合约地址
const MINT_FUNCTION_DATA = "0x1249c58b"; 
const GAS_AMOUNT = parseEther(process.env.GAS_AMOUNT); // 小号 gas 金额，从 .env 文件中获取
const MINT_ACCOUNT_COUNT = parseInt(process.env.MINT_ACCOUNT_COUNT, 10); // mint 小号的数量，从 .env 获取
const MINT_PER_ACCOUNT = parseInt(process.env.MINT_PER_ACCOUNT, 10); // 每个小号需要 mint 的次数，从 .env 获取
const THREAD_COUNT = parseInt(process.env.THREAD_COUNT, 10); // 线程数量，从 .env 获取

// ERC-20 标准 ABI，用于与合约交互
const ERC20_ABI = [
    "function transfer(address to, uint amount) public returns (bool)",
    "function balanceOf(address owner) view returns (uint)",
    "function decimals() view returns (uint8)" 
];

const provider = new JsonRpcProvider("https://mainnet.base.org");
const mainWallet = new Wallet(PRIVATE_KEY, provider);

// 保存地址和私钥的txt文件
const saveFilePath = "./minted_accounts.txt";

// 保存地址和私钥的函数
async function saveAccountData(address, privateKey, threadId) {
    const accountData = `${address} ---- ${privateKey}\n`; // 地址 ---- 私钥 格式
    fs.appendFileSync(saveFilePath, accountData); // 每次保存都会追加内容
    console.log(`[线程 ${threadId}] 地址 ${address} 和私钥已保存到文本.`);
}

// 延迟函数
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// mint 逻辑，作为 worker 内的任务执行
async function mintTokenWithNewAccount(threadId) {
    try {
        // 1. 生成新的地址和私钥
        const newWallet = Wallet.createRandom();
        const newAddress = newWallet.address;
        const newPrivateKey = newWallet.privateKey;

        console.log(`[线程 ${threadId}] 新地址: ${newAddress}`);
        console.log(`[线程 ${threadId}] 新私钥: ${newPrivateKey}`);

        // 2. 从主账户向新地址转账 gas
        const transferTx = await mainWallet.sendTransaction({
            to: newAddress,
            value: GAS_AMOUNT
        });

        await transferTx.wait();
        console.log(`[线程 ${threadId}] gas 已成功转移至新地址: ${newAddress}`);

        // 3. 小号使用 gas 去 mint
        const newWalletConnected = newWallet.connect(provider);
        const contract = new Contract(CONTRACT_ADDRESS, ERC20_ABI, newWalletConnected);

        let mintCount = 0; // 记录成功 mint 的次数
        let firstMintSuccess = false; // 标记是否已经成功保存过账户

        while (mintCount < MINT_PER_ACCOUNT) {
            try {
                const feeData = await provider.getFeeData();
                const gasPrice = feeData.gasPrice || feeData.maxFeePerGas; // 获取当前 gas 费用

                console.log(`[线程 ${threadId}] 当前 gas 费用: ${gasPrice.toString()}`);

                const tx = {
                    to: CONTRACT_ADDRESS,
                    data: MINT_FUNCTION_DATA,
                    value: parseEther("0"), 
                    gasPrice: gasPrice
                };

                const transaction = await newWalletConnected.sendTransaction(tx);
                console.log(`[线程 ${threadId}] 交易铸造哈希: ${transaction.hash}`);

                const receipt = await transaction.wait();
                console.log(`[线程 ${threadId}] 交易以区块形式铸造成功: ${receipt.blockNumber}`);

                mintCount++; // 成功 mint 一次，计数加 1
                console.log(`[线程 ${threadId}] 成功 mint 次数: ${mintCount}`);

                // 仅在第一次 mint 成功时保存地址和私钥
                if (!firstMintSuccess) {
                    await saveAccountData(newAddress, newPrivateKey, threadId);
                    firstMintSuccess = true; // 标记已经保存过
                }

            } catch (error) {
                console.log(`[线程 ${threadId}] 铸造失败, 1 分钟后重试...`);
                // mint 失败时延迟 1 分钟后继续重试
                await delay(60000);
            }
        }

        // 4. 将代币转回主账号
        console.log(`[线程 ${threadId}] 小号 ${newAddress} 完成 ${MINT_PER_ACCOUNT} 次 mint，开始将代币转回主账号`);

        const balance = await contract.balanceOf(newAddress);

        const formattedBalance = balance.toString(); 
        const originalString = formattedBalance; 
        const modifiedString = originalString.slice(0, -18);
        console.log(`[线程 ${threadId}] 小号代币余额: ${modifiedString} (${originalString})`);


        if (balance !== 0) {
            const transferBackTx = await contract.transfer(MAIN_ACCOUNT, balance);
            await transferBackTx.wait();
            console.log(`[线程 ${threadId}] 代币已成功转回主账号: ${MAIN_ACCOUNT}`);
        } else {
            console.log(`[线程 ${threadId}] 没有可转移的代币`);
        }

    } catch (error) {
        console.error(`[线程 ${threadId}] 流程失败`, error);
    }
}

// 主线程控制逻辑
async function main() {
    if (isMainThread) {
        console.log("主线程开始...");
        const threadQueue = [];
        for (let i = 0; i < MINT_ACCOUNT_COUNT; i++) {
            const worker = new Worker(__filename, { workerData: i + 1 }); // 新建线程，并传递线程编号
            threadQueue.push(worker);
            if (threadQueue.length >= THREAD_COUNT) {
                await new Promise(resolve => {
                    const finishedWorker = threadQueue.shift();
                    finishedWorker.on("exit", resolve);
                });
            }
        }
        while (threadQueue.length > 0) {
            await new Promise(resolve => {
                const finishedWorker = threadQueue.shift();
                finishedWorker.on("exit", resolve);
            });
        }

        console.log("所有 mint 任务完成.");
    } else {
        // Worker 执行 mint 任务，接收线程编号
        await mintTokenWithNewAccount(workerData);
        parentPort.close(); 
    }
}

main();
